
package jp.isisredirect.adk;

import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.nio.ByteBuffer;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.os.Handler;

// for adk support
//2.3.4
import com.android.future.usb.UsbAccessory;
import com.android.future.usb.UsbManager;
//3.1
//import android.hardware.usb.UsbAccessory;
//import android.hardware.usb.UsbManager;

abstract public class ADKBaseActivity extends Activity implements Runnable {
    static final String TAG = "ADKBaseActivity";
    private static final boolean DBG = true;

    static String action_usb_permission = null;

    private UsbManager mUsbManager;
    private PendingIntent mPermissionIntent;
    private boolean mPermissionRequestPending = false;
    private UsbAccessory mAccessory = null;
    private boolean isOpened = false;
    private ParcelFileDescriptor mFileDescriptor = null;
    private FileInputStream mInputStream = null;
    private FileOutputStream mOutputStream = null;

    private Runnable mSender = null;

    private final int SENDER_REPEAT_INTERVAL = 300;
    private Handler mSenderHandler = new Handler();

    private Queue<ByteBuffer> mQueue = null;

    private static final int MESSAGE_RECEIVED = 1;
    private static final int MESSAGE_CONNECTED = 2;
    private static final int MESSAGE_DISCONNECTED = 3;

    private Handler mADKHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_RECEIVED:
                    ByteBuffer o = (ByteBuffer) msg.obj;
                    onADKReceived(o);
                    break;

                case MESSAGE_CONNECTED:
                    onADKConnected();
                    break;

                case MESSAGE_DISCONNECTED:
                    onADKDisconnected();
                    break;
            }
        }
    };

    private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (DBG)
                Log.d(TAG, "UsbReceiver onResume " + action);
            if (action_usb_permission.equals(action)) {
                synchronized (this) {
                    UsbAccessory accessory = UsbManager.getAccessory(intent);
                    if (intent.getBooleanExtra(
                            UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        openAccessory(accessory);
                        if (DBG)
                            Log.d(TAG, "UsbReceiver openAccessory" + accessory);

                        Message m = Message.obtain(mADKHandler, MESSAGE_CONNECTED);
                        mADKHandler.sendMessage(m);
                    } else {
                        if (DBG)
                            Log.d(TAG, "permission denied for accessory "
                                    + accessory);
                    }
                    mPermissionRequestPending = false;
                }
            } else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
                UsbAccessory accessory = UsbManager.getAccessory(intent);
                if (accessory != null && accessory.equals(mAccessory)) {
                    if (DBG)
                        Log.d(TAG, "UsbReceiver closeAccessory" + accessory);
                    closeAccessory();
                    Message m = Message.obtain(mADKHandler, MESSAGE_DISCONNECTED);
                    mADKHandler.sendMessage(m);
                }
            }
        }
    };

    public ADKBaseActivity(String aup) {
        super();
        action_usb_permission = aup;
    }

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate");

        UsbAccessoryActivity.setLaunched(true);

        mUsbManager = UsbManager.getInstance(this);
        mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(
                action_usb_permission), 0);
        IntentFilter filter = new IntentFilter(action_usb_permission);
        filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
        registerReceiver(mUsbReceiver, filter);

        if (getLastNonConfigurationInstance() != null) {
            mAccessory = (UsbAccessory) getLastNonConfigurationInstance();
            openAccessory(mAccessory);
        }

        setContentViewByRes();
        if (mAccessory != null) {
            Message m = Message.obtain(mADKHandler, MESSAGE_CONNECTED);
            mADKHandler.sendMessage(m);
        } else {
            Message m = Message.obtain(mADKHandler, MESSAGE_DISCONNECTED);
            mADKHandler.sendMessage(m);
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if (DBG)
            Log.d(TAG, "onResume");
        {
            if (mInputStream != null && mOutputStream != null) {
                if (DBG)
                    Log.d(TAG, "onResume" + mInputStream + mOutputStream);
                return;
            }

            UsbAccessory[] accessories = mUsbManager.getAccessoryList();
            UsbAccessory accessory = (accessories == null ? null
                    : accessories[0]);
            if (accessory != null) {
                if (mUsbManager.hasPermission(accessory)) {
                    if (DBG)
                        Log.d(TAG, "onResume openAccessory" + accessory);
                    openAccessory(accessory);
                    Message m = Message.obtain(mADKHandler, MESSAGE_CONNECTED);
                    mADKHandler.sendMessage(m);
                } else {
                    synchronized (mUsbReceiver) {
                        if (!mPermissionRequestPending) {
                            if (DBG)
                                Log.d(TAG, "onResume mPermissionRequestPending"
                                        + mPermissionRequestPending);
                            mUsbManager.requestPermission(accessory,
                                    mPermissionIntent);
                            mPermissionRequestPending = true;
                        }
                    }
                }
            } else {
                if (DBG)
                    Log.d(TAG, "mAccessory is null");
            }
        }
    }

    @Override
    public void onPause() {
        if (DBG)
            Log.d(TAG, "onPause");
        super.onPause();
        closeAccessory();
        Message m = Message.obtain(mADKHandler, MESSAGE_DISCONNECTED);
        mADKHandler.sendMessage(m);
    }

    @Override
    public void onDestroy() {
        if (DBG)
            Log.d(TAG, "onDestroy");
        closeAccessory();
        unregisterReceiver(mUsbReceiver);
        UsbAccessoryActivity.setLaunched(false);
        super.onDestroy();
    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        if (mAccessory != null) {
            return mAccessory;
        } else {
            return super.onRetainNonConfigurationInstance();
        }
    }

    public void run() {
        int ret = 0;
        byte[] buffer = new byte[16384];
        if (0 < buffer.length) {
            while (ret >= 0) {
                try {
                    if (mInputStream != null) {
                        ret = mInputStream.read(buffer);
                    } else {
                        if (DBG)
                            Log.d(TAG, "run mInputStream null");
                        // loose break!
                        break;
                    }
                } catch (IOException e) {
                    // loose break!
                    break;
                }
                if (0 < ret) {
                    ByteBuffer rec_buffer = ByteBuffer.allocate(ret);
                    rec_buffer.put(buffer, 0, ret);
                    Message m = Message.obtain(mADKHandler, MESSAGE_RECEIVED);
                    m.obj = rec_buffer;
                    mADKHandler.sendMessage(m);
                }
            }
        } else {
            if (DBG)
                Log.d(TAG, "run no buffer");
        }
        if (DBG)
            Log.d(TAG, "run end");
    }

    // for control
    abstract protected void setContentViewByRes();

    // for accessory
    private final void openAccessory(UsbAccessory accessory) {
        if (DBG)
            Log.d(TAG, "openAccessory");
        mFileDescriptor = mUsbManager.openAccessory(accessory);
        if (mFileDescriptor != null) {
            isOpened = true;
            mAccessory = accessory;
            FileDescriptor fd = mFileDescriptor.getFileDescriptor();
            mInputStream = new FileInputStream(fd);
            if (mInputStream == null) {
                if (DBG)
                    Log.d(TAG, "mInputStream not opened");
                isOpened = false;
            } else {
                if (DBG)
                    Log.d(TAG, "mInputStream " + mInputStream);
            }
            mOutputStream = new FileOutputStream(fd);
            if (mOutputStream == null) {
                isOpened = false;
                if (DBG)
                    Log.d(TAG, "mOutputStream not opened");
            } else {

            }

            mQueue = new ConcurrentLinkedQueue<ByteBuffer>();

            Thread thread = new Thread(null, this, "ADKThread");
            thread.start();

            mSender = new Runnable() {
                public void run() {
                    while (!mQueue.isEmpty()) {
                        send(mQueue.poll());
                    }

                    mSenderHandler.postDelayed(this, SENDER_REPEAT_INTERVAL);
                }
            };
            mSenderHandler.postDelayed(mSender, SENDER_REPEAT_INTERVAL);

            if (DBG)
                Log.d(TAG, "accessory opened");

        } else {
            isOpened = false;
            if (DBG)
                Log.d(TAG, "accessory open fail");
        }
    }

    private final void closeAccessory() {
        if (DBG)
            Log.d(TAG, "accessory close");
        try {
            isOpened = false;
            if (mInputStream != null) {
                mInputStream.close();
            }
            if (mOutputStream != null) {
                mOutputStream.close();
            }
            if (mFileDescriptor != null) {
                mFileDescriptor.close();
            }
            if (mSenderHandler != null) {
                mSenderHandler.removeCallbacks(mSender);
            }
        } catch (IOException e) {
        } finally {
            mFileDescriptor = null;
            mInputStream = null;
            mOutputStream = null;
            mAccessory = null;
            mSender = null;
            mQueue = null;
        }
    }

    private final void send(ByteBuffer buffer) {
        if (mOutputStream != null) {
            try {
                Log.d(TAG, "send " + buffer.position() + buffer.limit());
                byte[] b = new byte[buffer.limit()];
                buffer.flip();
                Log.d(TAG, "send " + buffer.position() + " " + buffer.limit() + " " + b.length);
                buffer.get(b);
                mOutputStream.write(b);
            } catch (IOException e) {
                if (DBG)
                    Log.e(TAG, "write failed", e);
            }
        }
    }

    // ADK connection events
    abstract protected void onADKConnected();

    abstract protected void onADKDisconnected();

    // ADK data receive event
    abstract protected void onADKReceived(ByteBuffer buffer);

    // /
    public boolean isADKConnected() {
        return isOpened;
    }

    public void sendADKData(ByteBuffer buffer) {
        if (mQueue != null) {
            mQueue.add(buffer);
        }
    }

}
